package s63.kong.shop.modules.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import s63.kong.shop.pojo.AjaxResult;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Resource
    private UserDetailsService userDetailsService;
    @Resource
    private ObjectMapper objectMapper;
    @Resource
    private TokenFilter tokenFilter;

    @Bean//使用spring security提供的哈希函数算法对密码进行加密和验证
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    //认证
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //从数据库查询用户以及对应的权限
        auth.userDetailsService(userDetailsService);
    }

    //授权
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //关闭csrf保护，前后分离项目不需要
        http.csrf().disable();
        //无状态session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        //api配置
        http.authorizeRequests()
                //所有人都可以访问的api
                .antMatchers("doc.html","/login","logout").permitAll()
                //其他的api都需要登陆后访问
                .anyRequest().authenticated();

        //异常处理
        http.exceptionHandling()
                //认证失败（登陆失败或无效token）
                .authenticationEntryPoint((request, response, e) ->
                        out(response,401,"认证失败，请重新登录"))
                //授权失败
                .accessDeniedHandler((request, response, e) ->
                        out(response,403,String.format("无权访问,请联系管理员。url:%s,method:%s",request.getRequestURI(),request.getMethod())));
        //添加自定义token过滤器到spring security的UsernamePasswordAuthenticationFilter过滤器之前
        http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);
    }

    private void out(HttpServletResponse response,int code,String msg) throws IOException {
        response.setStatus(code);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(objectMapper.writeValueAsString(AjaxResult.error(code,msg)));
        response.getWriter().close();
    }
}
